package com.ice.gateway.app.filter;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.fastjson.JSONObject;
import com.ice.api.system.feign.IAuthService;
import com.iceframework.core.entity.Result;
import com.iceframework.core.support.security.entity.IJWTInfo;
import com.iceframework.core.support.security.entity.PermissionInfo;
import com.iceframework.core.support.security.util.JwtUtils;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * 过滤器
 * 
 * @author Palo
 *
 */
@Configuration
public class AccessGatewayFilter implements GlobalFilter {

	protected final Log logger = LogFactory.getLog(getClass());

	@Autowired
	private IAuthService authService;

	@Value("${web.ignore.startWith}")
	private String startWith;
	
	@Value("${web.jwt.token-header}")
    private String tokenHeader;

	private static final String GATE_WAY_PREFIX = "authority/jwt";

	@Override
	public Mono<Void> filter(ServerWebExchange serverWebExchange, GatewayFilterChain gatewayFilterChain) {
		LinkedHashSet<URI> requiredAttribute = serverWebExchange
				.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
		ServerHttpRequest request = serverWebExchange.getRequest();
		String requestUri = request.getPath().pathWithinApplication().value();
		if (requiredAttribute != null) {
			Iterator<URI> iterator = requiredAttribute.iterator();
			while (iterator.hasNext()) {
				URI next = iterator.next();
				if (next.getPath().startsWith(GATE_WAY_PREFIX)) {
					requestUri = next.getPath().substring(GATE_WAY_PREFIX.length());
				}
			}
		}
		//final String method = request.getMethod().toString();
		ServerHttpRequest.Builder mutate = request.mutate();
		// 不进行拦截的地址
		if (isStartWith(requestUri)) {
			ServerHttpRequest build = mutate.build();
			return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build());
		}
		IJWTInfo user = null;
		try {
			user = getJWTUser(false, request, mutate);
			if(user == null) {
				logger.error("用户取Token过期:");
				return getVoidMono(serverWebExchange, Result.fail().setCode(Result.UNAUTHORIZED).setMsg("User Token Forbidden or Expired!"));
			}
		} catch (Exception e) {
			logger.error("用户取Token异常:"+e);
			return getVoidMono(serverWebExchange, Result.fail().setCode(Result.UNAUTHORIZED).setMsg("User Token Forbidden or Expired!"));
		}
		/*List<PermissionInfo> permissionIfs = userService.getAllPermissionInfo();
		// 判断资源是否启用权限约束
		Stream<PermissionInfo> stream = getPermissionIfs(requestUri, method, permissionIfs);
		List<PermissionInfo> result = stream.collect(Collectors.toList());
		PermissionInfo[] permissions = result.toArray(new PermissionInfo[] {});
		if (permissions.length > 0) {
			if (checkUserPermission(permissions, serverWebExchange, user)) {
				logger.info("User Forbidden!Does not has Permission!");
				return getVoidMono(serverWebExchange, new TokenForbiddenResponse("User Forbidden!Does not has Permission!"));
			}
		}*/
		// 申请客户端密钥头
		// mutate.header(serviceAuthConfig.getTokenHeader(),
		// serviceAuthUtil.getClientToken());
		
		ServerHttpRequest build = mutate.build();
		return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build());

	}

	/**
	 * 网关抛异常
	 *
	 * @param body
	 */
	@NotNull
	private Mono<Void> getVoidMono(ServerWebExchange serverWebExchange, Result body) {
		serverWebExchange.getResponse().setStatusCode(HttpStatus.OK);
		byte[] bytes = JSONObject.toJSONString(body).getBytes(StandardCharsets.UTF_8);
		DataBuffer buffer = serverWebExchange.getResponse().bufferFactory().wrap(bytes);
		return serverWebExchange.getResponse().writeWith(Flux.just(buffer));
	}

	/**
	 * 获取目标权限资源
	 *
	 * @param requestUri
	 * @param method
	 * @param serviceInfo
	 * @return
	 */
	@SuppressWarnings("unused")
	private Stream<PermissionInfo> getPermissionIfs(final String requestUri, final String method,
			List<PermissionInfo> serviceInfo) {
		return serviceInfo.parallelStream().filter(new Predicate<PermissionInfo>() {
			@Override
			public boolean test(PermissionInfo permissionInfo) {
				String uri = permissionInfo.getUri();
				if (uri.indexOf("{") > 0) {
					uri = uri.replaceAll("\\{\\*\\}", "[a-zA-Z\\\\d]+");
				}
				String regEx = "^" + uri + "$";
				return (Pattern.compile(regEx).matcher(requestUri).find()) && method.equals(permissionInfo.getMethod());
			}
		});
	}

	@SuppressWarnings("unused")
	private void setCurrentUserInfoAndLog(ServerWebExchange serverWebExchange, IJWTInfo user, PermissionInfo pm) {
		String host = serverWebExchange.getRequest().getRemoteAddress().toString();
		System.out.println("setCurrentUserInfoAndLog:host" + host);
		/*
		 * LogInfo logInfo = new LogInfo(pm.getMenu(), pm.getName(), pm.getUri(), new
		 * Date(), user.getId(), user.getName(), host);
		 */
		// DBLog.getInstance().setLogService(logService).offerQueue(logInfo);
	}

	/**
	 * 返回session中的用户信息
	 *
	 * @param request
	 * @param ctx
	 * @return
	 */
	private IJWTInfo getJWTUser(boolean checkToken,ServerHttpRequest request, ServerHttpRequest.Builder ctx) throws Exception {
		List<String> strings = request.getHeaders().getValuesAsList(tokenHeader);
		String authToken = null;
		if (strings != null && strings.size() != 0) {
			authToken = strings.get(0);
		}
		if (StringUtils.isEmpty(authToken)) {
			strings = request.getQueryParams().get("token");
			if (strings != null) {
				authToken = strings.get(0);
			}
		}
		ctx.header(tokenHeader, authToken);
		
		/*com.ice.authority.app.system.entity.Log log = new com.ice.authority.app.system.entity.Log();
		log.setId(UUID.randomUUID().toString());
		log.setIp(request.getURI().toString());
		log.setMethod(request.getMethod().toString());
		log.setParams(request.getMethodValue());
		log.setUsername(authToken);
		logService.saveLog(log);*/
		
		boolean bool = authService.checkAuthToken(authToken);
		if(bool || !checkToken) {
			return JwtUtils.getJWTInfoByToken(authToken);
		}else {
			return null;
		}
	}

	/*
	 * private boolean checkUserPermission(PermissionInfo[] permissions,
	 * ServerWebExchange ctx, IJWTInfo user) { List<PermissionInfo> permissionInfos
	 * = userService.getPermissionByUsername(user.getUserName()); PermissionInfo
	 * current = null; for (PermissionInfo info : permissions) { boolean anyMatch =
	 * permissionInfos.parallelStream().anyMatch(new Predicate<PermissionInfo>() {
	 * 
	 * @Override public boolean test(PermissionInfo permissionInfo) { return
	 * permissionInfo.getCode().equals(info.getCode()); } }); if (anyMatch) {
	 * current = info; break; } } if (current == null) { return true; } else { if
	 * (!RequestMethod.GET.toString().equals(current.getMethod())) {
	 * setCurrentUserInfoAndLog(ctx, user, current); } return false; } }
	 */

	/**
	 * URI是否以什么开头
	 *
	 * @param requestUri
	 * @return
	 */
	private boolean isStartWith(String requestUri) {
		boolean flag = false;
		for (String s : startWith.split(",")) {
			if (requestUri.startsWith(s)) {
				return true;
			}
		}
		return flag;
	}

	/**
	 * 网关抛异常
	 *
	 * @param body
	 * @param code
	 */
	@SuppressWarnings("unused")
	private Mono<Void> setFailedRequest(ServerWebExchange serverWebExchange, String body, int code) {
		serverWebExchange.getResponse().setStatusCode(HttpStatus.OK);
		return serverWebExchange.getResponse().setComplete();
	}
	
}
